package cn.cdm.service;


import cn.cdm.entity.dto.RedPacketDto;
import cn.cdm.redis.RedisService;
import cn.cdm.util.RedPacketUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;

/**
 * @author: zhonglinsen
 * @date: 2019/3/15
 */
@Slf4j
@Service
public class RedPacketService {

    private static final String keyPrefix="redis:red:packet:";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedService redService;

    @Autowired
    private RedisService redisService;


    /**
     * 发红包
     * @throws Exception
     */
    public String handOut(RedPacketDto dto) throws Exception {
        if (dto.getTotal()>0 && dto.getAmount()>0){
            // 根据传入的红包总金额和红包个数，生成随机金额
            List<Integer> list= RedPacketUtils.divideRedPackage(dto.getAmount(),dto.getTotal());

            //生成红包全局唯一标识,并将随机金额存入Redis的List队列
            String timestamp=String.valueOf(System.nanoTime());
            // 生成 整个红包的全局唯一标识 redis:red:packet:10010:43026010240600 【时间戳，随机金额列表】
            String redId = new StringBuffer(keyPrefix).append(dto.getUserId()).append(":").append(timestamp).toString();

            // 将每个红包的放入Redis的List队列中，将每个红包的金额存入Redis的List数据结构中
//            redisTemplate.opsForList().leftPushAll(redId,list);
            redisService.set(redId, list, -1);

            // 设置红包总个数的Redis的key= redis:red:packet:10010:43026010240600:total 【total, 红包总个数】
            String redTotalKey = redId+":total";
            // 将总人数放入Redis的String中
//            redisTemplate.opsForValue().set(redTotalKey,dto.getTotal());
            redisService.set(redTotalKey, dto.getTotal(), -1);

            log.info("打印1：handOut");

            //  调用数据库的红包服务，异步记录红包发出的记录-包括红包个数与随机金额
            redService.recordRedPacket(dto,redId,list);

            return redId;
        }else{
            throw new Exception("系统异常-分发红包-参数不合法!");
        }
    }

    /**
     * 加分布式锁的情况
     * 抢红包-分“点”与“抢”处理逻辑
     * @throws Exception
     */
//    @Override
//    public BigDecimal rob(Integer userId,String redId) throws Exception {
//        ValueOperations valueOperations=redisTemplate.opsForValue();
//
//        //用户是否抢过该红包
//        Object obj=valueOperations.get(redId+userId+":rob");
//        if (obj!=null){
//            return new BigDecimal(obj.toString());
//        }
//
//        //"点红包"
//        Boolean res=click(redId);
//        if (res){
//            //上锁:一个红包每个人只能抢一次随机金额；一个人每次只能抢到红包的一次随机金额  即要永远保证 1对1 的关系
//            final String lockKey=redId+userId+"-lock";
//            Boolean lock=valueOperations.setIfAbsent(lockKey,redId);
//            redisTemplate.expire(lockKey,24L,TimeUnit.HOURS);
//            try {
//                if (lock) {
//
//                    //"抢红包"-且红包有钱
//                    Object value=redisTemplate.opsForList().rightPop(redId);
//                    if (value!=null){
//                        //红包个数减一
//                        String redTotalKey = redId+":total";
//
//                        Integer currTotal=valueOperations.get(redTotalKey)!=null? (Integer) valueOperations.get(redTotalKey) : 0;
//                        valueOperations.set(redTotalKey,currTotal-1);
//
//
//                        //将红包金额返回给用户的同时，将抢红包记录入数据库的RedRobRecord表中与缓存
//                        BigDecimal result = new BigDecimal(value.toString()).divide(new BigDecimal(100));
//                        redService.recordRobRedPacket(userId,redId,new BigDecimal(value.toString()));
//
//                        // 存入Redis缓存
//                        valueOperations.set(redId+userId+":rob",result,24L,TimeUnit.HOURS);
//
//                        log.info("当前用户抢到红包了：userId={} key={} 金额={} ",userId,redId,result);
//                        return result;
//                    }
//
//                }
//            }catch (Exception e){
//                throw new Exception("系统异常-抢红包-加分布式锁失败!");
//            }
//        }
//        return null;
//    }

    /**
     * 不加分布式锁的情况
     * 抢红包-分“点”与“抢”处理逻辑
     * @param userId
     * @param redId
     * @return
     * @throws Exception
     */
    public BigDecimal rob(Integer userId,String redId) throws Exception {

        String key = redId + ":" + userId + ":rob";
        Object object = redisService.get(key, Object.class);

        if (object!=null) {
            log.info("打印：用户已经抢到红包，直接返回");
            return new BigDecimal(object.toString());
        }

        //"点红包" // todo 抢红包表中，设置唯一索引，数据库中应该显示“同一个用户只抢到一个红包”，但另外的红包应该是丢失了
        Boolean res=click(redId);
        if (res){
            //"抢红包"-且红包有钱
            Object value = null;
            List list = redisService.get(redId, List.class);
            if (CollectionUtils.isNotEmpty(list)) {
                value = list.get(0);
            }

            if (value!=null){
                list.remove(0);
                redisService.set(redId, list, -1);
                //红包个数减一
                String redTotalKey = redId+":total";
                redisService.decr(redTotalKey);

                //将红包金额返回给用户的同时，将抢红包记录入数据库与缓存
                BigDecimal result = new BigDecimal(value.toString()).divide(new BigDecimal(100));
                // 异步存入数据库，将抢红包的用户、红包唯一标识、抢的金额
                redService.recordRobRedPacket(userId,redId,new BigDecimal(value.toString()));

                // 将抢红包的用户，抢的金额存入Redis，并设置1小时之后过期
                redisService.set(redId + ":" + userId+":rob", result, 3600);

                log.info("当前用户抢到红包了：userId={} key={} 金额={} ",userId,redId,result);
                return result;
            }

        }
        return null;
    }


    /**
     * 点红包-返回true，则代表红包还有，个数>0
     */
    private Boolean click(String redId) {
        String redTotalKey = redId+":total";
        Integer total = redisService.get(redTotalKey, Integer.class);

        if (total!=null && total > 0) {
            return true;
        }
        return false;
    }
}




























